#include "head.h"

// 服务端接收文件函数
// 第一个参数网络socket
// 第二个参数要接收的文件名
int recvFile(int socket_fd, transport_protocol_t *msg)
{
    // 先接收文件名，根据filename,user_id,parent_id查表判断当前用户目录下有没有同名文件
    char file_name[60] = {0};
    recv(socket_fd, file_name, sizeof(file_name), MSG_WAITALL);

    char temp_path[60] ={0};
    strcpy(temp_path,msg->curr_path);
    if(strcmp(temp_path,"/") == 0){
        bzero(temp_path,sizeof(temp_path));
    }
    char url[1024];
    sprintf(url,"%s/%s",temp_path,file_name);
    //printf("----url----:%s\n",url);

    char sql_statement[1500];
    memset(sql_statement, 0, sizeof(sql_statement));
    sprintf(sql_statement, "select * from bd_file where filename='%s' and user_id='%s' and url='%s' and delete_flag=0 and upload_flag=0",
            file_name, msg->user_id,url);
    //printf("SQL1:%s\n", sql_statement);
    //  对sql语句检错
    //printf("%s", mysql_error(&msg->my_sql));
    mysql_query(&msg->my_sql, sql_statement);
    MYSQL_RES *result = mysql_store_result(&msg->my_sql);
    MYSQL_ROW row = mysql_fetch_row(result);
    int exit_flag = 0;
    if(row != 0){   //row不为0，说明在当前用户目录下有同名且未删除、上传成功的文件
        mysql_free_result(result);
        exit_flag = 1;
        send(socket_fd,&exit_flag,sizeof(int),MSG_NOSIGNAL);
        //printf("puts命令执行结束\n");
        return 0;
    }
    mysql_free_result(result);
    send(socket_fd,&exit_flag,sizeof(int),MSG_NOSIGNAL);

    // 接收hash值前，先接收用户发送的文件是否存在标记
    int is_file = 0;
    recv(socket_fd, &is_file, sizeof(int), MSG_WAITALL);
    if (is_file != 0)
    {
        //printf("puts命令执行结束_1\n");
        return 0;
    }

    // 接收客户端上传文件的hash值
    char hash_val[100];
    recv(socket_fd, hash_val, sizeof(hash_val), MSG_WAITALL);
    // printf("接收文件的hash值:%s\n", hash_val);

    // 查表判断是否有此文件
    memset(sql_statement, 0, sizeof(sql_statement));
    sprintf(sql_statement, "select * from bd_file where file_hash='%s' and delete_flag=0", hash_val);
    //printf("SQL2:%s\n", sql_statement);
    mysql_query(&msg->my_sql, sql_statement);
    //  对sql语句检错
    printf("%s", mysql_error(&msg->my_sql));
    MYSQL_RES *result1 = mysql_store_result(&msg->my_sql);
    my_ulonglong row1 = mysql_num_rows(result1);
    mysql_free_result(result1);

    if (row1 == 0) // rows == 0,查表失败，服务端没有此文件
    {
        // 打开要接收的文件
        int file_fd = open(hash_val, O_RDWR | O_CREAT | O_APPEND, 0600);

        ftruncate(file_fd, 0);

        // 获取新创建的文件大小
        struct stat st;
        memset(&st, 0, sizeof(st));
        fstat(file_fd, &st);
        // 通知客户端，当前服务器上没有此文件，
        off_t curr_size = st.st_size;
        send(socket_fd, &curr_size, sizeof(off_t), MSG_NOSIGNAL);

        // 接收文件大小
        off_t file_size = 0;
        recv(socket_fd, &file_size, sizeof(off_t), MSG_WAITALL);

        // 首先在mmap之前调整文件大小, 让文件足够大
        ftruncate(file_fd, file_size);

        // mmap内存映射
        void *p = mmap(NULL, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, file_fd, 0);
        // 接收文件
        off_t recv_size = recv(socket_fd, p, file_size, MSG_WAITALL);
        ERROR_CHECK(recv_size, -1, "recv");
        // 切割文件大小为recv接收的大小
        ftruncate(file_fd, recv_size);

        // 查表找到父目录id
        memset(sql_statement, 0, sizeof(sql_statement));
        sprintf(sql_statement, "select file_id from bd_file where url='%s' and user_id=%s", msg->curr_path,msg->user_id);
        //printf("SQL3:%s\n", sql_statement);
        mysql_query(&msg->my_sql, sql_statement);

        //  对sql语句检错
        printf("%s\n", mysql_error(&msg->my_sql));

        MYSQL_RES *result2 = mysql_store_result(&msg->my_sql);
        MYSQL_ROW row2 = mysql_fetch_row(result2);
        char parent_id[10] = {0}; // 父目录id
        strcpy(parent_id, row2[0]);
        mysql_free_result(result2);

        // url文件的路径（客户端视角）
        char buf2[1024] = {0};
        strcpy(buf2, msg->curr_path);
        if (strcmp(msg->curr_path, "/") == 0)
        {
            bzero(msg->curr_path, sizeof(msg->curr_path));
        }
        char url[1024];
        sprintf(url, "%s/%s", msg->curr_path, msg->buf);

        // printf("parent_id_95row:%s\n", parent_id);
        //  recv接收的文件大小小于客户端发来的大小，说明文件未传输完成  ok
        if (recv_size < file_size)
        {
            // sql插入upload_flag=-1的数据(未上传完成)
            memset(sql_statement, 0, sizeof(sql_statement));
            sprintf(sql_statement, "insert into bd_file values(NULL,'%s','%s',%s,'%s',2,'%s',0,-1)",
                    msg->buf, msg->user_id, parent_id, url, hash_val);
            //printf("SQL4:%s\n", sql_statement);
            mysql_query(&msg->my_sql, sql_statement);
            //  对sql语句检错
            printf("%s", mysql_error(&msg->my_sql));

            // 通知用户
            send(socket_fd, "文件未上传成功_1\n", 100, MSG_NOSIGNAL);
            //printf("客户端意外断开,文件未上传成功,puts命令执行结束_1\n");

            // 关闭mmap映射和文件fd
            munmap(p, file_size);
            close(file_fd);
            return 0;
        }

        // 走到这里：说明接收到完整的文件       ok
        // sql插入upload_flag=0的数据(上传完成)
        memset(sql_statement, 0, sizeof(sql_statement));
        sprintf(sql_statement, "insert into bd_file values(NULL,'%s','%s',%s,'%s',2,'%s',0,0)",
                msg->buf, msg->user_id, parent_id, url, hash_val);
        //printf("SQL5:%s\n", sql_statement);
        mysql_query(&msg->my_sql, sql_statement);
        //  对sql语句检错
        //printf("%s", mysql_error(&msg->my_sql));
        // 通知用户
        send(socket_fd, "文件上传成功_1\n", 100, MSG_NOSIGNAL);
        char msg2[] = "puts命令执行结束_1";
        LOG_WRITE(1, msg2);

        // 关闭mmap映射和文件fd
        munmap(p, file_size);
        close(file_fd);
        return 0;
    }

    else // 走到这：rows != 0，查表成功，有此文件(不确定是否完整)
    {
        // 查表找到此文件的upload_flag标记位，和所属用户user_id
        memset(sql_statement, 0, sizeof(sql_statement));
        sprintf(sql_statement, "select upload_flag,user_id,parent_id from bd_file where file_hash='%s' and delete_flag=0",
                hash_val);
        //printf("SQL6:%s\n", sql_statement);
        mysql_query(&msg->my_sql, sql_statement);
        //  对sql语句检错
        printf("%s", mysql_error(&msg->my_sql));
        MYSQL_RES *result3 = mysql_store_result(&msg->my_sql);
        MYSQL_ROW row3 = mysql_fetch_row(result3);

        //char upload_flag[2] = {0}; // 上传是否完成标记位
        //strcpy(upload_flag, row3[0]);
         //printf("表中文件的upload_flag:%s\n", upload_flag);

        char user_id[20] = {0}; // 此文件的所属用户id
        strcpy(user_id, row3[1]);
        //printf("表中文件的user_id:%s\n", user_id);

        char parent_id[10] = {0}; // 此文件的父目录id
        strcpy(parent_id, row3[2]);
        //printf("表中文件的parent_id:%s\n", parent_id);

        mysql_free_result(result3);

        // printf("程序运行到了这里\n");

        if (strcmp(row3[0], "0") == 0) // 上传标记位为0，表明此文件完整，直接返回极速秒传    ok
        {

            // 打开已上传完整的文件
            int file_fd = open(hash_val, O_RDWR | O_CREAT | O_APPEND, 0600);
            // 获取完整的文件大小
            struct stat st;
            memset(&st, 0, sizeof(st));
            fstat(file_fd, &st);
            // 通知客户端，当前服务器有完整的文件，发送一个完整的文件大小
            off_t curr_size = st.st_size;
            send(socket_fd, &curr_size, sizeof(off_t), MSG_NOSIGNAL);

            // printf("msg->curr_path:%s\n", msg->curr_path);
            //  printf("msg->user_id:%s\n",msg->user_id);

            //  查表找到父目录id
            memset(sql_statement, 0, sizeof(sql_statement));
            sprintf(sql_statement, "select file_id from bd_file where url='%s' and user_id='%s'",
                    msg->curr_path, msg->user_id);
            //printf("SQL4:%s\n", sql_statement);
            mysql_query(&msg->my_sql, sql_statement);
            //  对sql语句检错
            //printf("%s", mysql_error(&msg->my_sql));
            MYSQL_RES *result4 = mysql_store_result(&msg->my_sql);
            MYSQL_ROW row4 = mysql_fetch_row(result4);

            char parent_id2[10] = {0}; // 要上传文件的父目录id
            strcpy(parent_id2, row4[0]);
            // printf("要上传文件的父目录,即当前路径的file_id:%s\n", parent_id2);

            mysql_free_result(result4);

            // url文件的路径（客户端视角）
            char buf2[1024] = {0};
            strcpy(buf2, msg->curr_path);
            if (strcmp(msg->curr_path, "/") == 0)
            {
                bzero(msg->curr_path, sizeof(msg->curr_path));
            }
            char url[1024];
            sprintf(url, "%s/%s", msg->curr_path, msg->buf);

            // 如果此文件所属用户id和客户端用户id不一样  ok
            if (strcmp(msg->user_id, user_id) != 0)
            {
                // printf("msg->user_id:%s\n", msg->user_id);
                // printf("parent_id:%s\n", parent_id);
                memset(sql_statement, 0, sizeof(sql_statement));
                sprintf(sql_statement, "insert into bd_file values(NULL,'%s','%s',%s,'%s',2,'%s',0,0)",
                        msg->buf, msg->user_id, parent_id2, url, hash_val);
                mysql_query(&msg->my_sql, sql_statement);
                //  对sql语句检错
                //printf("%s", mysql_error(&msg->my_sql));
                // 通知用户
                send(socket_fd, "极速秒传成功\n", 100, MSG_NOSIGNAL);

                char msg1[] = "puts命令执行结束";
                LOG_WRITE(1, msg1);

                close(file_fd);
                return 0;
            }

            else // 此文件所属用户id和客户端用户id一样  ok
            {
                // file_id当前路径的文件id
                char file_id[10] = {0};
                strcpy(file_id, parent_id2);
                //printf("row[0]:%s\n",row4[0]);
                //printf("parent_id:%s\n",parent_id);
                // parent_id表中查到的文件父目录id
                // 这两个id不一样，说明用户在另一个文件夹下上传其他目录下存在相同的文件
                if (strcmp(parent_id, row4[0]) != 0)
                {
                    memset(sql_statement, 0, sizeof(sql_statement));
                    sprintf(sql_statement, "insert into bd_file values(NULL,'%s','%s',%s,'%s',2,'%s',0,0)",
                            msg->buf, msg->user_id, parent_id2, url, hash_val);
                    mysql_query(&msg->my_sql, sql_statement);
                    //  对sql语句检错
                    //printf("%s", mysql_error(&msg->my_sql));
                    // 通知用户
                    send(socket_fd, "极速秒传成功\n", 100, MSG_NOSIGNAL);
                    close(file_fd);
                    return 0;
                }
                else if(strcmp(parent_id,row4[0]) == 0){
                    memset(sql_statement, 0, sizeof(sql_statement));
                    sprintf(sql_statement, "insert into bd_file values(NULL,'%s','%s',%s,'%s',2,'%s',0,0)",
                            msg->buf, msg->user_id, parent_id2, url, hash_val);
                    mysql_query(&msg->my_sql, sql_statement);
                    //  对sql语句检错
                    //printf("%s", mysql_error(&msg->my_sql));
                    // 通知用户
                    send(socket_fd, "极速秒传成功\n", 100, MSG_NOSIGNAL);
                    close(file_fd);
                    return 0;
                }
                // 想客户端发送一句话
                send(socket_fd, "不可重复上传该文件\n", 100, MSG_NOSIGNAL);
                // printf("puts命令执行结束\n");
                close(file_fd);
                return 0;
            }
        }

        else // 上传标记位为-1，表明此文件不完整，需要断点续传
        {
            // 打开已接收的不完整文件
            int file_fd = open(hash_val, O_RDWR | O_APPEND, 0600);

            // 获取不完整的文件大小
            struct stat st;
            memset(&st, 0, sizeof(st));
            fstat(file_fd, &st);

            // 接受文件前把已上传的文件大小发送给客户端
            off_t updated_size = st.st_size;
            //printf("已下载的文件大小:%ld\n", updated_size);
            send(socket_fd, &updated_size, sizeof(off_t), MSG_NOSIGNAL);

            // 接收续传的文件大小
            off_t continue_file_size = 0;
            recv(socket_fd, &continue_file_size, sizeof(off_t), MSG_WAITALL);
            // printf("continue_file_size:%ld\n", continue_file_size);

            // updated_num 表示已下载文件对页大小4096取整的页数
            off_t updated_num = updated_size / 4096;
            // 调整文件大小为源文件大小
            off_t source_file_size = continue_file_size + updated_num * 4096;
            // printf("源文件大小：%ld\n", source_file_size);
            ftruncate(file_fd, source_file_size);

            // mmap内存映射
            void *p = mmap(NULL, continue_file_size, PROT_READ | PROT_WRITE,
                           MAP_SHARED, file_fd, updated_num * 4096);
            // 接收文件
            char msg3[] = "--开始从断点接收数据";
            LOG_WRITE(1, msg3);

            int recv_size = recv(socket_fd, p, continue_file_size, MSG_WAITALL);
            // printf("------recv_size:%d\n", recv_size);
            ERROR_CHECK(recv_size, -1, "recv");

            // 切割文件大小为recv接受的大小+已接收的完整页大小
            // printf("从断点接收完后完整大小：%ld\n", recv_size + updated_num * 4096);
            ftruncate(file_fd, recv_size + updated_num * 4096);

            // 再次获取文件大小，判断是否续传完成
            memset(&st, 0, sizeof(st));
            fstat(file_fd, &st);

            // 查表找到父目录id
            memset(sql_statement, 0, sizeof(sql_statement));
            sprintf(sql_statement, "select file_id from bd_file where url='%s' and user_id='%s'",
                    msg->curr_path, msg->user_id);
            //printf("SQL7:%s\n", sql_statement);
            mysql_query(&msg->my_sql, sql_statement);
            //  对sql语句检错
            //printf("%s", mysql_error(&msg->my_sql));

            MYSQL_RES *result5 = mysql_store_result(&msg->my_sql);
            MYSQL_ROW row5 = mysql_fetch_row(result5);
            char parent_id[10] = {0}; // 父目录id
            strcpy(parent_id, row5[0]);
            // printf("parent_id:%s\n", parent_id);

            mysql_free_result(result5);

            // url文件的路径（客户端视角）
            char buf2[1024] = {0};
            strcpy(buf2, msg->curr_path);
            if (strcmp(msg->curr_path, "/") == 0)
            {
                bzero(msg->curr_path, sizeof(msg->curr_path));
            }
            char url[1024];
            sprintf(url, "%s/%s", msg->curr_path, msg->buf);

            // 再次获取的文件大小还是小于完整源文件大小
            if (st.st_size < source_file_size)
            {
                // 想客户端发送一句话
                send(socket_fd, "文件未上传成功\n", 100, MSG_NOSIGNAL);
                //printf("客户端意外断开,文件未上传成功,puts命令执行结束_319\n");
                LOG_WRITE(1, "客户端意外断开,文件未上传成功,puts命令执行结束_319\n");

                munmap(p, continue_file_size);
                close(file_fd);
                return 0;
            }

            // 走到这里：再次获取的文件大小和源文件大小一样，上传成功
            if (strcmp(msg->user_id, user_id) != 0) //  如果此文件所属用户id和客户端用户id不一样
            {
                // 为该客户端用户在表中也插入一条数据，标记此传输完整文件也属于该用户
                memset(sql_statement, 0, sizeof(sql_statement));
                sprintf(sql_statement, "insert into bd_file values(NULL,'%s','%s','%s','%s',2,'%s',0,0)",
                        msg->buf, msg->user_id, parent_id, url, hash_val);
                //printf("SQL8:%s\n", sql_statement);
                mysql_query(&msg->my_sql, sql_statement);
                //  对sql语句检错
                //printf("%s", mysql_error(&msg->my_sql));

                memset(sql_statement, 0, sizeof(sql_statement));
                sprintf(sql_statement, "update bd_file set upload_flag=0 where file_hash='%s'", hash_val);
                //printf("SQL9:%s\n", sql_statement);
                mysql_query(&msg->my_sql, sql_statement);
                //  对sql语句检错
                //printf("%s", mysql_error(&msg->my_sql));

                // 向客户端发送一句话
                send(socket_fd, "文件上传成功_2\n", 100, MSG_NOSIGNAL);
                //printf("puts命令执行结束_342\n");
            }
            else // 此文件所属用户id和客户端用户id一样      ok
            {
                // 修改表中upload_flag标记位为0，表示文件上传成功
                memset(sql_statement, 0, sizeof(sql_statement));
                sprintf(sql_statement, "update bd_file set upload_flag=0 where file_hash='%s' and user_id='%s'",
                        hash_val, msg->user_id);
                //printf("SQL10:%s\n", sql_statement);
                mysql_query(&msg->my_sql, sql_statement);
                //  对sql语句检错
                //printf("%s", mysql_error(&msg->my_sql));
                // 向客户端发送一句话
                send(socket_fd, "文件上传成功_2\n", 100, MSG_NOSIGNAL);
                //printf("puts命令执行结束\n");
            }

            munmap(p, continue_file_size);
            close(file_fd);
            return 0;
        }
    }
}
